package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.lkd.common.VMSystem;
import com.lkd.config.ConsulConfig;
import com.lkd.config.TopicConfig;
import com.lkd.contract.OrderCheck;
import com.lkd.contract.VendoutContract;
import com.lkd.contract.VendoutData;
import com.lkd.dao.OrderDao;
import com.lkd.feign.UserService;
import com.lkd.vo.*;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.OrderEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.VMService;
import com.lkd.service.OrderService;
import com.lkd.utils.DistributedLock;
import com.lkd.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    private VMService vmService;
    @Autowired
    private UserService userService;
    @Autowired
    private MqttProducer mqttProducer;
    @Autowired
    private ConsulConfig consulConfig;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public OrderEntity getByOrderNo(String orderNo) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(OrderEntity::getOrderNo, orderNo);
        return this.getOne(qw);
    }

    @Override
    @Transactional
    public OrderEntity createOrder(PayVO payVO) {


        Boolean hasCapacity =
                vmService.hasCapacity(payVO.getInnerCode(), Long.valueOf(payVO.getSkuId()));
        if (hasCapacity == null || !hasCapacity) {
            throw new LogicException("商品已经售空!");
        }

        //进入下单流程，加锁
        DistributedLock distributedLock =
                new DistributedLock(consulConfig.getConsulRegisterHost(), consulConfig.getConsulRegisterPort());
        //创建锁对象
        String lockName = "lock-" + payVO.getInnerCode();
        DistributedLock.LockContext lock = distributedLock.getLock(lockName, 60 * 5);
        if(!lock.isGetLock()){
            //没有获取到锁
            throw new LogicException("售货机正在使用中，请稍后重试!");
        }

        //存放分布式锁的session
        String session = lock.getSession();
        redisTemplate.opsForValue().set(VMSystem.VM_LOCK_KEY_PREF + lockName,session
                ,Duration.ofMinutes(5));

        //1.订单基本信息 支付、openId
        OrderEntity orderEntity = new OrderEntity();
        //openId
        orderEntity.setOpenId(payVO.getOpenId());
        //支付状态
        orderEntity.setPayStatus(VMSystem.PAY_STATUS_NOPAY);
        //状态
        orderEntity.setStatus(VMSystem.ORDER_STATUS_CREATE);
        //订单编号 注意服务器时间不对等，可能生成同样的数据 解决方案：后边加上一个序列号redis
        orderEntity.setOrderNo(payVO.getInnerCode() + System.nanoTime());
        //设置支付类型
        orderEntity.setPayType("2");

        //2.售货机信息
        VmVO vmInfo = vmService.getVMInfo(payVO.getInnerCode());
        if (vmInfo == null) {
            throw new LogicException("售货机不存在!");
        }
        BeanUtils.copyProperties(vmInfo, orderEntity);
        orderEntity.setAddr(vmInfo.getNodeAddr());

        //3.商品信息
        SkuVO sku = vmService.getSku(payVO.getSkuId());
        if (sku == null) {
            throw new LogicException("商品不存在!");
        }
        BeanUtils.copyProperties(sku, orderEntity);
        orderEntity.setAmount(sku.getRealPrice());

        //4.处理分账金额
        PartnerVO partner = userService.getPartner(vmInfo.getOwnerId());
        if (partner == null) {
            throw new LogicException("合作商不存在!");
        }

        Integer ratio = partner.getRatio(); //扩大了100倍
        //避免精度丢失，全程使用BigDecimal
        BigDecimal bigDecimalRatio = new BigDecimal(ratio); //分成比例
        BigDecimal bigDecimalPrice = new BigDecimal(sku.getRealPrice()); //价格
        int bill = bigDecimalPrice.multiply(bigDecimalRatio).divide(new BigDecimal(100),
                        0, RoundingMode.HALF_UP)
                .intValue(); //价格0.01 计算出来会是0
        orderEntity.setBill(bill);

        save(orderEntity);

        OrderCheck orderCheck = new OrderCheck();
        orderCheck.setOrderNo(orderEntity.getOrderNo());

        //发送延迟消息
        try {
            mqttProducer.send("$delayed/900/" + TopicConfig.ORDER_CHECK_TOPIC,
                    2,
                    orderCheck);
        } catch (JsonProcessingException e) {
           log.error("发送延迟消单消息失败",e);
        }

        return orderEntity;
    }

    @Override
    public void vendout(String orderNo, Long skuId,String innerCode) {

        VendoutContract vendoutContract = new VendoutContract();
        vendoutContract.setInnerCode(innerCode);

        VendoutData vendoutData = new VendoutData();
        vendoutData.setSkuId(skuId);
        vendoutData.setOrderNo(orderNo);

        vendoutContract.setVendoutData(vendoutData);

        //发送延迟消息
        try {
            mqttProducer.send(TopicConfig.VMS_VENDOUT_TOPIC,
                    2,
                    vendoutContract);
        } catch (JsonProcessingException e) {
            log.error("发送出货消息到售货机微服务失败",e);
        }
    }

}
